import 'dart:io';

import 'package:common_utils/common_utils.dart';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/instance_manager.dart';
import 'package:path_provider/path_provider.dart';
import 'package:x_common_utils/cache_encryption_decrypt_util/cache_encryption_decrypt_util.dart';
import 'package:xinxuan/config/app_config.dart';
import 'package:xinxuan/controllers/common_page_controller.dart';
import 'package:xinxuan/router/app_routers.dart';
import 'package:xinxuan/utils/cache_key_util.dart';
import 'package:get/get.dart' as getx;
import 'package:xinxuan/utils/param_util.dart';

class HttpUtil {
  
  static final CommonPageController commonPageController = Get.find();

  static HttpUtil? _instance;

  static HttpUtil get instance => HttpUtil();

  HttpUtil._internal();

  Dio? dio;
  BaseOptions? options;

  static HttpUtil getInstance() {
    if (_instance == null) _instance = HttpUtil._internal();
    return _instance!;
  }

  /*
   * config it and create
   */
  HttpUtil() {
    //BaseOptions、Options、RequestOptions 都可以配置参数，优先级别依次递增，且可以根据优先级别覆盖参数
    options = new BaseOptions(
      baseUrl: AppConfig.apiHost,
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: 45000,
      //响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: 45000,
      //Http请求头.
      headers: {
        "App-Version": commonPageController.appVersion.value,
        "Device-Type": commonPageController.appDeviceType.value,
        // "Accept": 'application/vnd.magic.v2+json'
      },
      contentType: 'application/x-www-form-urlencoded',
      responseType: ResponseType.json,
    );

    dio = new Dio(options);
    dio!.interceptors.add(LogInterceptor(requestBody: true));
    /// 拦截器
    dio!.interceptors.add(InterceptorsWrapper(
      /* 请求之前 */
      onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
        options.queryParameters = ParamUtil.param(options.queryParameters);
        return handler.next(options);
      },
      /* 响应之前 */
      onResponse: (Response response, ResponseInterceptorHandler handler) {
        return handler.next(response);
      },
      /* 错误之前 */
      onError: (DioError error, ErrorInterceptorHandler handler) {
        _handleResponse(error.response!);
        return handler.next(error);
      }
    ));
    dio!.interceptors.add(DioCacheManager(CacheConfig(baseUrl: AppConfig.apiHost)).interceptor);
  }

  /// get请求
  get(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.get(
        url, 
        queryParameters: data,
        options: buildCacheOptions(
          Duration(days: 7),
          maxStale: Duration(days: 7),
          forceRefresh: true,
          options: Options(
            headers: {
              "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
            }
          )
        ),
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }

  /// post请求
  post(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.post(
        url, 
        queryParameters: data,
        options: Options(
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        )
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }

  /// post请求 使用body传参
  postBody(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.post(
        url, 
        data: data,
        options: Options(
          contentType: 'application/json',
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        ),
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }
  
  /// put请求
  put(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.put(
        url, 
        queryParameters: data,
        options: Options(
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        )
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }

  /// put请求
  putBody(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.put(
        url, 
        data: data,
        options: Options(
          contentType: 'application/json',
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        )
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }
  
  /// del请求
  del(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.delete(
        url, 
        queryParameters: data,
        options: Options(
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        )
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }

  /// 表单提交
  formPost(url, {data, loading = true}) async {
    String token = AppConfig.token;
    // String token = await CacheEncryptionDecryptUtil.getStringCacheEncryption(key: CacheKeyUtil.token , defaultValue: '');
    if (loading) {
      EasyLoading.show();
    }
    print('《$url 发起请求-------》');
    Response response;
    try {
      response = await dio!.post(
        url, 
        data: data, 
        options: Options(
          contentType: 'application/json',
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        )
      );
      if (loading) {
        EasyLoading.dismiss();
      }
      return response.data;
    } on DioError catch (e) {
      formatError(e);
      return null;
    }
  }

  /// 下载文件
  Future<int> downloadFile({required String urlPath, required String savePath, required CancelToken cancelToken, ValueChanged<double>? percentChange}) async {
    // File file = new File(savePath);
    try {
      /// 发起下载请求
      // Response response = await dio!.get(urlPath,
      //   onReceiveProgress: (received, total) {
      //     if (total != -1) {
      //       double percent = received / total;
      //       if (percentChange != null) {
      //         percentChange(double.parse('${NumUtil.getNumByValueDouble(percent, 2)}'));
      //       }
      //     }
      //   },
      //   options: Options(
      //     responseType: ResponseType.bytes,
      //     followRedirects: false,
      //   ),
      //   cancelToken: cancelToken
      // );
      // file.writeAsBytesSync(response.data);
      String token = AppConfig.token;
      Response response = await dio!.download(
        urlPath, 
        savePath,
        options: Options(
          contentType: 'multipart/form-data',
          headers: {
            "Authorization": !TextUtil.isEmpty(token) ? "Bearer $token" : '',
          }
        ),
        onReceiveProgress: (received, total) {
          if (total != -1) {
            double percent = received / total;
            if (percentChange != null) {
              percentChange(double.parse('${NumUtil.getNumByValueDouble(percent, 2)}'));
            }
          }
        }
      );
      return response.statusCode!;
    } catch (e) {
      // formatError(e);
      if (cancelToken.isCancelled) {
        print('已取消请求');
      }
      return 0;
    }
  }

  /// 下载安卓更新包
  Future<File> downloadAndroid(String url, String version) async {
    /// 创建存储文件
    Directory storageDir = await getApplicationDocumentsDirectory();
    String storagePath = storageDir.path;
    File file = new File('$storagePath/v$version.apk');
    try {
      /// 发起下载请求
      Response response = await Dio().get(url,
        onReceiveProgress: (received, total) {
          if (total != -1) {
            
          }
        },
        options: Options(
          responseType: ResponseType.bytes,
          followRedirects: false,
        )
      );
      file.writeAsBytesSync(response.data);
      return file;
    } catch (e) {
      print(e);
      return File('');
    }
  }

  /*
   * error统一处理
   */
  void formatError(DioError e) {
    // LogUtil.e(e);
    if (e.type == DioErrorType.connectTimeout) {
      /// 连接超时  
      print('......连接超时');
    } else if (e.type == DioErrorType.sendTimeout) {
      /// 请求超时
      print('......请求超时');
    } else if (e.type == DioErrorType.receiveTimeout) {
      /// 响应超时
      print('......响应超时');
    } else if (e.type == DioErrorType.response) {
      /// 出现异常
      print('......出现异常');
    } else if (e.type == DioErrorType.cancel) {
      /// 请求取消
      print('......请求取消.....');
    } else {
      /// 未知错误
      print('......未知错误');
    }
    // if (EasyLoading.isShow) {
    //   EasyLoading.dismiss();
    // }
  }

  /// 响应前统一处理
  _handleResponse(Response response) async {
    EasyLoading.dismiss();
    /// token 失效
    if (response.statusCode == 401) {
      AppConfig.token = '';
      await CacheEncryptionDecryptUtil.removeByKey(key: CacheKeyUtil.token);
      commonPageController.checkLogin();
      getx.Get.toNamed('${AppRouters.COMMON_PAGE}${AppRouters.LOGIN_PAGE}');
    }
    /// 未填写收货地址
    if (response.statusCode == 301) {
      getx.Get.toNamed(
        '${AppRouters.COMMON_PAGE}${AppRouters.MY_INFO_PAGE}${AppRouters.ADDRESS_LIST_PAGE}',
        arguments: {
          'isSelectAddress': false
        }
      );
    }
    /// 未添加抢购银行卡
    if (response.statusCode == 302) {
      getx.Get.toNamed('${AppRouters.COMMON_PAGE}${AppRouters.MY_INFO_PAGE}${AppRouters.BANK_CARD_LIST_PAGE}');
    }
    /// 未实名认证
    if (response.statusCode == 305) {
      getx.Get.toNamed('${AppRouters.COMMON_PAGE}${AppRouters.MY_INFO_PAGE}${AppRouters.AUTHENTICATION_PAGE}');
    }
    /// 未抢购
    if (response.statusCode == 306) {
      getx.Get.toNamed('${AppRouters.COMMON_PAGE}${AppRouters.SECKILL_PAGE}');
    }
    EasyLoading.showToast('${response.data['message']}');
  }

  /*
   * 取消请求
   *
   * 同一个cancel token 可以用于多个请求，当一个cancel token取消时，所有使用该cancel token的请求都会被取消。
   * 所以参数可选
   */
  void cancelRequests(CancelToken token) {
    token.cancel("cancelled");
  }
}